/****************************************************************************/
/*  File:       PackageDescriptorFactory.java                               */
/*  Author:     F. Georges - fgeorges.org                                   */
/*  Date:       2009-06-15                                                  */
/*  Tags:                                                                   */
/*      Copyright (c) 2009 Florent Georges (see end of file.)               */
/* ------------------------------------------------------------------------ */


package org.expath.pkg.saxon;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Build a {@link PackageDescriptor} from a package descriptor.
 *
 * @author Florent Georges
 * @date   2009-06-15
 */
public class PackageDescriptorFactory
        implements ContentHandler
{
    public PackageDescriptor read(File f)
            throws PackageException
    {
        try {
            XMLReader reader = XMLReaderFactory.createXMLReader();
            reader.setContentHandler(this);
            InputSource src = new InputSource(new FileInputStream(f));
            reader.parse(src);
            return myPkg;
        }
        catch ( FileNotFoundException ex ) {
            throw new PackageException("Package descriptor not found: " + f, ex);
        }
        catch ( IOException ex ) {
            throw new PackageException("Error reading the package descriptor: " + f, ex);
        }
        catch ( SAXException ex ) {
            throw new PackageException("Error parsing the package descriptor: " + f, ex);
        }
    }

    public void setDocumentLocator(Locator locator)
    {
        // TODO: Should use it to report errors...
    }

    public void startDocument()
            throws SAXException
    {
        // nothing
    }

    public void endDocument()
            throws SAXException
    {
        // nothing
    }

    public void startPrefixMapping(String prefix, String uri)
            throws SAXException
    {
        // nothing
    }

    public void endPrefixMapping(String prefix)
            throws SAXException
    {
        // nothing
    }

    public void startElement(String uri, String local, String qname, Attributes atts)
            throws SAXException
    {
        if ( myCatalog != null ) {
            myCatalog.append("<");
            myCatalog.append(local);
            addAttributes(atts);
            myCatalog.append(">");
        }
        else if ( PKG_NS.equals(uri) ) {
            if ( "package".equals(local) ) {
                myPkg = new PackageDescriptor();
            }
            else if ( "module".equals(local) ) {
                myCurrModule = new ModuleDescriptor();
                myPkg.add(myCurrModule);
                String ver = atts.getValue("", "version");
                if ( ver == null ) {
                    throw new SAXException("pkg:module/@version is not there");
                }
                myCurrModule.setVersion(ver);
                String name = atts.getValue("", "name");
                if ( name == null ) {
                    throw new SAXException("pkg:module/@name is not there");
                }
                myCurrModule.setName(name);
            }
            else if ( isStylesheetDescriptor(local) ) {
                myCurrStyle = new StylesheetDescriptor();
                if ( isJavaWrapper(local) ) {
                    myCurrModule.addWrapper(myCurrStyle);
                }
                else {
                    myCurrModule.addStylesheet(myCurrStyle);
                }
            }
            else if ( myWithinSaxon && "dep".equals(local) ) {
                if ( ! atts.getValue("", "type").equals("jar") ) {
                    throw new SAXException("pkg:saxon/pkg:dep/@type is not 'jar'");
                }
                myCurrDep = new SaxonDepDescriptor();
                myCurrModule.addDependency(myCurrDep);
            }
            else if ( "import-uri".equals(local)
                    || "file".equals(local)
                    || "title".equals(local)
                    || "home".equals(local)
                    || "href".equals(local)
                    || "namespace".equals(local)
                    || "catalog-prefix".equals(local)
                    || ( myWithinSaxon && "function".equals(local) )
                    || ( myWithinSaxon && "library".equals(local) ) ) {
                myBuffer = new StringBuilder();
            }
            else if ( ! myWithinSaxon && "saxon".equals(local) ) {
                myWithinSaxon = true;
            }
            else {
                throw new SAXException("Unknown element: {" + uri + "}" + local);
            }
        }
        else if ( CATALOG_NS.equals(uri) && "catalog".equals(local) ) {
            // TODO: Use a dedicated content handler to serialize to a byte
            // array.
            myCatalog = new StringBuilder();
            myCatalog.append("<catalog xmlns='");
            myCatalog.append(CATALOG_NS);
            myCatalog.append("'");
            addAttributes(atts);
            myCatalog.append(">");
        }
        else {
            throw new SAXException("Unknown element: {" + uri + "}" + local);
        }
    }

    private boolean isStylesheetDescriptor(String local)
    {
        return "xsl".equals(local)
            || "xquery".equals(local)
            || isJavaWrapper(local);
    }

    private boolean isJavaWrapper(String local)
    {
        return myWithinSaxon
            && ( "xsl-wrapper".equals(local) || "xquery-wrapper".equals(local) );
    }

    private void addAttributes(Attributes atts)
    {
        for ( int i = atts.getLength() - 1; i >= 0; --i ) {
            myCatalog.append(" ");
            myCatalog.append(atts.getQName(i));
            myCatalog.append("='");
            // should escape...
            myCatalog.append(atts.getValue(i));
            myCatalog.append("'");
        }
    }

    public void endElement(String uri, String local, String qname)
            throws SAXException
    {
        if ( myCatalog != null ) {
            myCatalog.append("</");
            myCatalog.append(local);
            myCatalog.append(">");
            if ( "catalog".equals(local) ) {
                myCatalog.append("\n");
                String cat = myCatalog.toString();
                myCatalog = null;
                try {
                    myCurrModule.setCatalog(cat.getBytes("utf-8"));
                }
                catch ( UnsupportedEncodingException ex ) {
                    // should not happen
                    throw new SAXException(ex);
                }
            }
        }
        else if ( ! PKG_NS.equals(uri) ) {
            // nothing
        }
        else if ( "module".equals(local) ) {
            myCurrModule = null;
        }
        else if ( isStylesheetDescriptor(local) ) {
            myCurrStyle = null;
        }
        else if ( "dep".equals(local) ) {
            myCurrDep = null;
        }
        else if ( "import-uri".equals(local) ) {
            myCurrStyle.setImportUri(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "file".equals(local) ) {
            myCurrStyle.setFile(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "title".equals(local) ) {
            if ( myCurrDep == null ) {
                myCurrModule.setTitle(myBuffer.toString());
                myBuffer = null;
            }
            else {
                myCurrDep.setTitle(myBuffer.toString());
                myBuffer = null;
            }
        }
        else if ( "home".equals(local) ) {
            myCurrDep.setHome(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "href".equals(local) ) {
            myCurrDep.setHref(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "namespace".equals(local) ) {
            myCurrStyle.setNamespace(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "catalog-prefix".equals(local) ) {
            myCurrModule.setCatalogPrefix(myBuffer.toString());
            myBuffer = null;
        }
        else if ( myWithinSaxon && "function".equals(local) ) {
            myCurrModule.addFunctionClass(myBuffer.toString());
            myBuffer = null;
        }
        else if ( myWithinSaxon && "library".equals(local) ) {
            myCurrModule.addLibraryClass(myBuffer.toString());
            myBuffer = null;
        }
        else if ( "saxon".equals(local) ) {
            myWithinSaxon = false;
        }
        else {
            // TODO: What to do?
        }
    }

    public void characters(char[] ch, int start, int length)
            throws SAXException
    {
        if ( myCatalog != null ) {
            myCatalog.append(ch, start, length);
        }
        else if ( myBuffer != null ) {
            myBuffer.append(ch, start, length);
        }
        else {
            // TODO: Check if there is any non-whitespace characters
        }
    }

    public void ignorableWhitespace(char[] ch, int start, int length)
            throws SAXException
    {
        if ( myCatalog != null ) {
            myCatalog.append(ch, start, length);
        }
    }

    public void processingInstruction(String target, String data)
            throws SAXException
    {
        if ( myCatalog != null ) {
            myCatalog.append("<?");
            myCatalog.append(target);
            myCatalog.append(" ");
            myCatalog.append(data);
            myCatalog.append("?>");
        }
    }

    public void skippedEntity(String name)
            throws SAXException
    {
        throw new SAXException("Unknown entity: " + name);
    }

    private PackageDescriptor    myPkg;
    private ModuleDescriptor     myCurrModule;
    private StylesheetDescriptor myCurrStyle;
    private SaxonDepDescriptor   myCurrDep;
    private StringBuilder        myBuffer;
    private StringBuilder        myCatalog;
    private boolean              myWithinSaxon = false;
    private static final String  PKG_NS     = "http://expath.org/mod/expath-pkg";
    private static final String  CATALOG_NS = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
}


/* ------------------------------------------------------------------------ */
/*  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT.               */
/*                                                                          */
/*  The contents of this file are subject to the Mozilla Public License     */
/*  Version 1.0 (the "License"); you may not use this file except in        */
/*  compliance with the License. You may obtain a copy of the License at    */
/*  http://www.mozilla.org/MPL/.                                            */
/*                                                                          */
/*  Software distributed under the License is distributed on an "AS IS"     */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See    */
/*  the License for the specific language governing rights and limitations  */
/*  under the License.                                                      */
/*                                                                          */
/*  The Original Code is: all this file.                                    */
/*                                                                          */
/*  The Initial Developer of the Original Code is Florent Georges.          */
/*                                                                          */
/*  Contributor(s): none.                                                   */
/* ------------------------------------------------------------------------ */
